//==========================================================================
// Copyright (c) 2000-2009,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#define __CGRAFIXENGINE_H__
#include "CGrafixEventBuffer.h"
#include "_CGrafixEventBuffer.cpp"

ECode CGrafixEventBuffer::constructor(
    /* [in] */ Int32 contextId,
    /* [in] */ GrafixObjectStack * stack)
{
    m_contextId = contextId;
    m_stack = stack;

    m_get = m_set = 0;

    m_timer = FALSE;
    m_paint = FALSE;
    m_closed = FALSE;
    m_shine = FALSE;
    m_bufferChanged = FALSE;
    m_orientationChanged = FALSE;

    m_availableEventHeader.Initialize();
    m_unavailingEventHeader.Initialize();

    ECode ec = DzCreateEventObj(FALSE, UNSIGNALED, &m_event);
    if (GFX_FAILED(ec)) {
        return ec;
    }

    memset(&m_lastEvent, 0, sizeof(GrafixObjectEvent));

    return ec;
}

CGrafixEventBuffer::~CGrafixEventBuffer()
{
    g_lock.Lock();

    if (m_event) {
        m_event->Release();
        m_event = NULL;
    }

    assert(m_availableEventHeader.IsEmpty());

    while (!m_unavailingEventHeader.IsEmpty()) {
        EventLink * link = (EventLink *)m_unavailingEventHeader.First();
        link->Detach();
        free(link);
    }

    // remove from the eventbuffer link in the layer.
    //
    if (m_stack) {
        Detach();
    }

    m_stack = NULL;

    GFX_EXPRESSION(DEBUG_DESTRUCTOR, kprintf("~CGrafixEventBuffer()\n"));

    g_lock.Unlock();
}

Boolean CGrafixEventBuffer::HandleCloseEvent(
    /* [out] */ GrafixObjectEvent * pEvent)
{
    assert(pEvent);
    assert(m_stack);

    Int32 index;
    Void* element;
    CGrafixObject* closedObject = NULL;

    fusion_vector_foreach_reverse(element, index, m_stack->m_windows) {
        CGrafixObject* grafixObjecct = (CGrafixObject *)element;

        if (!grafixObjecct->m_alive
            && !grafixObjecct->m_sendClosedMessage
            && grafixObjecct->m_contextId == m_contextId) {
            assert(grafixObjecct->m_eventBuffer == this);

            closedObject = grafixObjecct;

            if (closedObject->m_type != GrafixObjectType_Main) {
                break;
            }
        }
    }

    if (closedObject) {
        closedObject->m_sendClosedMessage = TRUE;

        if (GrafixObjectType_Main == closedObject->m_type) {
            pEvent->type = GrafixObjectEventType_ApplicationClosed;
        }
        else {
            pEvent->type = GrafixObjectEventType_Closed;
        }

        pEvent->objId = closedObject->m_objId;
        pEvent->userDefinedId = closedObject->m_userDefinedId;
        pEvent->userDefinedHandle = closedObject->m_userDefinedHandle;

        assert(GrafixObjectVisibleSwitch_None == closedObject->m_visibleSwitch);

        return TRUE;
    }

    return FALSE;
}

Boolean CGrafixEventBuffer::HandleBufferChangedEvent(
    /* [out] */ GrafixObjectEvent * pEvent)
{
    assert(pEvent);

    Int32 index;
    Void* element;
    fusion_vector_foreach_reverse(element, index, m_stack->m_windows) {
        CGrafixObject* grafixObject = (CGrafixObject *)element;

        if (grafixObject->m_contextId == m_contextId
                && grafixObject->m_bufferChanged) {
            grafixObject->m_bufferChanged = FALSE;
            pEvent->type = GrafixObjectEventType_BufferChanged;
            pEvent->objId = grafixObject->m_objId;
            pEvent->userDefinedId = grafixObject->m_userDefinedId;
            pEvent->userDefinedHandle = grafixObject->m_userDefinedHandle;

            return TRUE;
        }
    }

    return FALSE;
}

Boolean CGrafixEventBuffer::HandleOrientationChangedEvent(
    /* [out] */ GrafixObjectEvent * pEvent)
{
    assert(pEvent);

    Int32 index;
    Void* element;
    fusion_vector_foreach_reverse(element, index, m_stack->m_windows) {
        CGrafixObject* grafixObject = (CGrafixObject *)element;

        if (grafixObject->m_contextId == m_contextId
                && grafixObject->m_orientationChanged) {
            grafixObject->m_orientationChanged = FALSE;
            pEvent->type = GrafixObjectEventType_ScreenTransform;
            pEvent->keyValue = grafixObject->m_stack->m_orientation;
            pEvent->objId = grafixObject->m_objId;
            pEvent->userDefinedId = grafixObject->m_userDefinedId;
            pEvent->userDefinedHandle = grafixObject->m_userDefinedHandle;

            return TRUE;
        }
    }

    return FALSE;
}

Boolean CGrafixEventBuffer::HandlePaintEvent(
    /* [out] */ GrafixObjectEvent* pEvent)
{
    assert(pEvent);

    Int32 index;
    Void* element;
    fusion_vector_foreach_reverse(element, index, m_stack->m_windows) {
        CGrafixObject* grafixObject = (CGrafixObject *)element;

        if (grafixObject->m_contextId == m_contextId
                && grafixObject->m_needPaint) {
            GFX_EXPRESSION(DEBUG_PAINT,
                kprintf("grafixobject 0x%08x(%S) send paint message\n",
                grafixObject, grafixObject->m_title));

            grafixObject->m_needPaint = FALSE;
            grafixObject->m_paintStatus = PaintStatus_Begin;
            pEvent->type = GrafixObjectEventType_Paint;
            pEvent->objId = grafixObject->m_objId;
            pEvent->userDefinedId = grafixObject->m_userDefinedId;
            pEvent->userDefinedHandle = grafixObject->m_userDefinedHandle;

            return TRUE;
        }
    }

    return FALSE;
}

Boolean CGrafixEventBuffer::HandleShineEvent(
    /* [out] */ GrafixObjectEvent * pEvent)
{
    assert(pEvent);

    Int32 index;
    Void* element;
    fusion_vector_foreach_reverse(element, index, m_stack->m_windows) {
        CGrafixObject* grafixObject = (CGrafixObject *)element;

        if (grafixObject->m_contextId == m_contextId) {
            switch (grafixObject->m_visibleSwitch) {
            case GrafixObjectVisibleSwitch_Exposed:
                GFX_EXPRESSION(DEBUG_PAINT,
                    kprintf("grafixobject 0x%08x(%S) send exposed message\n",
                    grafixObject, grafixObject->m_title));

                grafixObject->m_visibleSwitch = GrafixObjectVisibleSwitch_None;
                grafixObject->m_exposed = TRUE;
                pEvent->type = GrafixObjectEventType_Exposed;
                pEvent->objId = grafixObject->m_objId;
                pEvent->userDefinedId = grafixObject->m_userDefinedId;
                pEvent->userDefinedHandle = grafixObject->m_userDefinedHandle;

                return TRUE;

            case GrafixObjectVisibleSwitch_CoveredUp:
                GFX_EXPRESSION(DEBUG_PAINT,
                    kprintf("grafixobject 0x%08x(%S) send covered up message\n",
                    grafixObject, grafixObject->m_title));

                assert(grafixObject->m_surface->m_flipRectangle->IsEmpty());

                grafixObject->m_visibleSwitch = GrafixObjectVisibleSwitch_None;
                grafixObject->m_exposed = FALSE;

                grafixObject->m_needPaint = FALSE;
                if (PaintStatus_Begin == grafixObject->m_paintStatus) {
                    grafixObject->m_paintStatus = PaintStatus_NotNeed;
                    grafixObject->m_animationFeasibility = AnimationFeasibility_None;
                }

                pEvent->type = GrafixObjectEventType_CoveredUp;
                pEvent->objId = grafixObject->m_objId;
                pEvent->userDefinedId = grafixObject->m_userDefinedId;
                pEvent->userDefinedHandle = grafixObject->m_userDefinedHandle;

                return TRUE;

            default:;
            }
        }
    }

    return FALSE;
}

Boolean CGrafixEventBuffer::HandleTimeEvent(
    /* [out] */ GrafixObjectEvent * pEvent)
{
    static UInt32 timerBaseCount = 0;

    FusionVector* grafixObjects = &m_stack->m_windows;
    UInt32 index;

    if (timerBaseCount >= (UInt32)grafixObjects->count) {
        timerBaseCount = 0;
    }

    index = timerBaseCount;

    do {
        CGrafixObject* grafixObject = (CGrafixObject *)grafixObjects->elements[index];

        if (grafixObject->m_contextId == m_contextId
                && grafixObject->m_haveTimeEvent) {
            for (Int32 i = 0; i < TIMER_COUNT; i++) {
                if (grafixObject->m_timeEvent[i]) {
                    pEvent->relativeX = grafixObject->m_timeEvent[i];
                    grafixObject->m_timeEvent[i] = 0;
                    pEvent->type = GrafixObjectEventType_Timer;
                    pEvent->objId = grafixObject->m_objId;
                    pEvent->userDefinedId = grafixObject->m_userDefinedId;
                    pEvent->userDefinedHandle = grafixObject->m_userDefinedHandle;
                    pEvent->timeId = grafixObject->m_timeData[i].id;

                    timerBaseCount = ++timerBaseCount % grafixObjects->count;

                    return TRUE;
                }
            }

            grafixObject->m_haveTimeEvent = FALSE;
        }

        index = ++index % grafixObjects->count;
    } while (index != timerBaseCount);

    return FALSE;
}

ECode CGrafixEventBuffer::GetEvent(
    /* [out] */ GrafixObjectEvent * pEvent)
{
    if (!pEvent) {
        GFX_ERROR_MESSAGE("Invalid argument");

        return E_INVALID_ARGUMENT;
    }

    WaitResult wr;
    ECode ec = NOERROR;

    g_lock.Lock();

    Lock();

    if (!m_stack) {
        GFX_ERROR_MESSAGE("grafixobject stack has been destoryed");
        ec = E_STACK_BE_DESTROYED;

        goto Exit;
    }

    if (m_bufferChanged) {
        if (HandleBufferChangedEvent(pEvent)) {
            goto Exit;
        }

        m_bufferChanged = FALSE;
    }

    if (m_shine) {
        if (HandleShineEvent(pEvent)) {
            goto Exit;
        }

        m_shine = FALSE;
    }

    if (m_orientationChanged) {
        if (HandleOrientationChangedEvent(pEvent)) {
            goto Exit;
        }

        m_orientationChanged = FALSE;
    }

    if (m_paint) {
        if (HandlePaintEvent(pEvent)) {
            goto Exit;
        }

        m_paint = FALSE;
    }

    if (m_closed) {
        if (HandleCloseEvent(pEvent)) {
            goto Exit;
        }

        m_closed = FALSE;
    }

    if (!m_availableEventHeader.IsEmpty()) {
        EventLink* link = (EventLink *)m_availableEventHeader.First();
        link->Detach();

        memcpy(pEvent, &link->event, sizeof(GrafixObjectEvent));

        m_unavailingEventHeader.InsertFirst(link);

        goto Exit;
    }

    while (m_set == m_get) {
        if (m_timer) {
            if (HandleTimeEvent(pEvent)) {
                goto Exit;
            }

            m_timer = FALSE;
        }

        Unlock();

        g_lock.Unlock();

        ec = m_event->Wait(&wr, NULL);
        if (GFX_FAILED(ec)) {
            goto Exit;
        }

        if (WaitResult_Interrupted == wr) {
            GFX_WARNING_MESSAGE("Wait eventbuffer be interrupt\n");

            return E_INTERRUPTED;
        }

        g_lock.Lock();

        Lock();

        if (m_bufferChanged) {
            if (HandleBufferChangedEvent(pEvent)) {
                goto Exit;
            }

            m_bufferChanged = FALSE;
        }

        if (m_shine) {
            if (HandleShineEvent(pEvent)) {
                goto Exit;
            }

            m_shine = FALSE;
        }

        if (m_orientationChanged) {
            if (HandleOrientationChangedEvent(pEvent)) {
                goto Exit;
            }

            m_orientationChanged = FALSE;
        }

        if (m_paint) {
            if (HandlePaintEvent(pEvent)) {
                goto Exit;
            }

            m_paint = FALSE;
        }

        if (m_closed) {
            if (HandleCloseEvent(pEvent)) {
                goto Exit;
            }

            m_closed = FALSE;
        }

        if (!m_availableEventHeader.IsEmpty()) {
            EventLink* link = (EventLink *)m_availableEventHeader.First();
            link->Detach();

            memcpy(pEvent, &link->event, sizeof(GrafixObjectEvent));

            m_unavailingEventHeader.InsertFirst(link);

            goto Exit;
        }
    }

    memcpy(pEvent, &m_events[m_get], sizeof(GrafixObjectEvent));
    m_get = (m_get + 1) % EVENTBUFFER_SIZE;

Exit:
    m_lastEvent = *pEvent;
    Unlock();

    g_lock.Unlock();

    return ec;
}

ECode CGrafixEventBuffer::AddEvent(
    /* [in] */ const GrafixObjectEvent & event,
    /* [in] */ GrafixObjectProperty status)
{
    ECode       ec = NOERROR;

    Lock();

    if (m_get != m_set
        && GrafixObjectEventType_StylusMove == event.type
        && (!(status & GrafixObjectProperty_CoalesceStylusMove))) {
        Int32 i = (m_set + EVENTBUFFER_SIZE - 1) % EVENTBUFFER_SIZE;
        if (m_events[i].objId == event.objId
            && m_events[i].type == GrafixObjectEventType_StylusMove) {
            assert(m_events[i].userDefinedId == event.userDefinedId
                && m_events[i].userDefinedHandle == event.userDefinedHandle);

            m_events[i].relativeX     = event.relativeX;
            m_events[i].relativeY     = event.relativeY;
            m_events[i].absoluteX   = event.absoluteX;
            m_events[i].absoluteY   = event.absoluteY;

            goto Exit;
        }
    }

    if (m_get == (m_set + 1) % EVENTBUFFER_SIZE) {
#ifdef _DEBUG
        ec =
#endif
        Notify(SIGNALED);

        assert(NOERROR == ec);
        // FULL
        //
        ec = S_BUFFER_TOO_SMALL;

        goto Exit;
    }

    memcpy(&m_events[m_set], &event, sizeof(GrafixObjectEvent));
    m_set = (m_set + 1) % EVENTBUFFER_SIZE;
#ifdef _DEBUG
    ec =
#endif
    Notify(SIGNALED);
    assert(NOERROR == ec);

Exit:
    Unlock();

    return ec;
}

ECode CGrafixEventBuffer::AddNotifyEvent(
    /* [in] */ Int32 appletId,
    /* [in] */ const GrafixObjectEvent & event)
{
    ECode   ec = NOERROR;

    assert(-1 != appletId);

    Lock();

    if (!m_stack) {
        GFX_ERROR_MESSAGE("grafixobject stack has been destoryed");
        ec = E_STACK_BE_DESTROYED;

        goto Exit;
    }

    EventLink * link;
    if (!m_unavailingEventHeader.IsEmpty()) {
        link = (EventLink *)m_unavailingEventHeader.First();
        link->Detach();
    }
    else {
        link = (EventLink *)malloc(sizeof(EventLink));
        if (!link) {
            GFX_EXPRESSION(ERRORLEVEL,
                kprintf("Not enough memory\n"));
            ec = E_OUT_OF_MEMORY;

            goto Exit;
        }
    }

    link->appletId = appletId;
    memcpy(&link->event, &event, sizeof(GrafixObjectEvent));
    m_availableEventHeader.InsertLast(link);

#ifdef _DEBUG
    ec =
#endif
    Notify(SIGNALED);
    assert(NOERROR == ec);

Exit:
    Unlock();

    return ec;
}

Void CGrafixEventBuffer::CleanEvent(
    /* [in] */ CGrafixObject * pGrafixObject)
{
    assert(m_stack);

    Lock();

    if (pGrafixObject->m_type == GrafixObjectType_Main) {
        EventLink* header = (EventLink *)m_availableEventHeader.First();
        while (header != &m_availableEventHeader) {
            EventLink* link = header;
            header = (EventLink *)header->Next();

            if (link->appletId == pGrafixObject->m_appletId) {
                link->Detach();
                m_unavailingEventHeader.InsertFirst((EventLink *)link);
            }
        }

        // clean time and paint event
        //
        Void* p;
        Int32 index;
        fusion_vector_foreach(p, index, m_stack->m_windows) {
            CGrafixObject* object = (CGrafixObject *)p;
            if (pGrafixObject->m_appletId == object->m_appletId) {
                object->m_haveTimeEvent = FALSE;
                for (int i = 0; i < TIMER_COUNT; i++) {
                    object->m_timeEvent[i] = 0;
                }

                object->m_needPaint = FALSE;
                assert(object->m_visibleSwitch != GrafixObjectVisibleSwitch_Exposed);

                Int32 header = m_set;
                Int32 tailer = m_get;
                Int32 id = object->m_objId;

                while (header != tailer) {
                    if (m_events[tailer].objId == id) {
                        m_events[tailer].objId = (UInt32)-1;
                        m_events[tailer].userDefinedId = 0;
                        m_events[tailer].userDefinedHandle = 0;
                    }

                    tailer = (tailer + 1) % EVENTBUFFER_SIZE;
                }
            }
        }
    }
    else {
        assert(pGrafixObject->m_eventBuffer == this);
        assert(pGrafixObject->m_contextId == m_contextId);

        Int32 header = m_set;
        Int32 tailer = m_get;
        Int32 id = pGrafixObject->m_objId;

        // clean time and paint event
        //
        pGrafixObject->m_haveTimeEvent = FALSE;
        for (int i = 0; i < TIMER_COUNT; i++) {
            pGrafixObject->m_timeEvent[i] = 0;
        }

        pGrafixObject->m_needPaint = FALSE;
        assert(pGrafixObject->m_visibleSwitch != GrafixObjectVisibleSwitch_Exposed);

        while (header != tailer) {
            if (m_events[tailer].objId == id) {
                m_events[tailer].objId = (UInt32)-1;
                m_events[tailer].userDefinedId = 0;
                m_events[tailer].userDefinedHandle = 0;
            }

            tailer = (tailer + 1) % EVENTBUFFER_SIZE;
        }

        EventLink* link = (EventLink *)m_availableEventHeader.First();
        EventLink* next = link;

        while (next != &m_availableEventHeader) {
            next = (EventLink *)link->Next();
            if (link->event.objId == id) {
                link->Detach();
                m_unavailingEventHeader.InsertFirst((EventLink *)link);
            }

            link = next;
        }
    }

    Unlock();
}

ECode CGrafixEventBuffer::Notify(
    /* [in] */ Int32 type)
{
    Lock();

    switch (type) {
        case NotifyType_Timer:
            m_timer = TRUE;
            break;

        case NotifyType_Paint:
            m_paint = TRUE;
            break;

        case NotifyType_Close:
            m_closed = TRUE;
            break;

        case NotifyType_Shine:
            m_shine = TRUE;
            break;

        case NotifyType_BufferChanged:
            m_bufferChanged = TRUE;
            break;

        case NotifyType_OrientationChanged:
            m_orientationChanged = TRUE;
            break;

        default:
            break;
    }

    Unlock();

    return m_event->Notify(SIGNALED);
}
